﻿using System;
using System.Text;

namespace BSharp.Compiler.Emit
{
	public class RaptorCompiler : IntermediateCompiler
	{
		private StringBuilder accum = new StringBuilder ();


		public RaptorCompiler (StringBuilder buff) {
			accum = buff;
		}

		public override void Compile (And and)
		{
			Append ("And {0}, {1}", TranslateRegister (and.Register1), TranslateRegister (and.Register2));
		}

		public override void Compile (Bor bor)
		{
			Append ("Or {0}, {1}", TranslateRegister (bor.Register1), TranslateRegister (bor.Register2));
		}

		public override void Compile (Add add)
		{
			Append ("Add {0}, {1}", TranslateRegister (add.Register1), TranslateRegister (add.Register2));
		}

		public override void Compile (Addi add)
		{
			Append ("Add_Immediate {0}, {1}", TranslateRegister (add.Register), add.Immediate);
		}

		public override void Compile (Sub sub)
		{
			Append ("Sub {0}, {1}", TranslateRegister (sub.Register1), TranslateRegister (sub.Register2));
		}

		public override void Compile (Mul mul)
		{
			Append ("Mul {0}, {1}", TranslateRegister (mul.Register1), TranslateRegister (mul.Register2));
		}
		
		public override void Compile (Muli mul)
		{
			Append ("Mul_Immediate {0}, {1}", TranslateRegister (mul.Register), TranslateRegister (mul.Immediate));
		}

		public override void Compile (Div div)
		{
			Append ("Div {0}, {1}", TranslateRegister (div.Register1), TranslateRegister (div.Register2));
		}

		public override void Compile (Mod mod)
		{
			Append ("Mod {0}, {1}", TranslateRegister (mod.Register1), TranslateRegister (mod.Register2));
		}

		public override void Compile (TestIfEqual teq)
		{
			Append ("Cmp {0}, {1}", TranslateRegister (teq.Register1), TranslateRegister (teq.Register2));
			Append ("Mov {0}, FLAGS", TranslateRegister (teq.Register1));
			Append ("And_Immediate {0}, 1", TranslateRegister (teq.Register1));
		}


		public override void Compile (TestIfNotEqual tne)
		{
			Append ("Cmp {0}, {1}", TranslateRegister (tne.Register1), TranslateRegister (tne.Register2));
			Append ("Mov {0}, FLAGS", TranslateRegister (tne.Register1));
			Append ("Not {0}", TranslateRegister (tne.Register1));
			Append ("And_Immediate {0}, 1", TranslateRegister (tne.Register1));
		}

		public override void Compile (TestIfGreaterThan tg)
		{	
			Append ("Cmp {0}, {1}", TranslateRegister (tg.Register1), TranslateRegister (tg.Register2));
			Append ("Mov {0}, FLAGS", TranslateRegister (tg.Register1));
			Append ("Not {0}", TranslateRegister (tg.Register1));
			Append ("And_Immediate  {0}, 2", TranslateRegister (tg.Register1));
		}

		public override void Compile (TestIfGreaterThanOrEqual tge)
		{
			Append ("Cmp {0}, {1}", TranslateRegister (tge.Register1), TranslateRegister (tge.Register2));
			Append ("Mov {0}, FLAGS", TranslateRegister (tge.Register1));
			Append ("And_Immediate  {0}, 3", TranslateRegister (tge.Register1));
			Append ("Xor_Immediate {0}, 2", TranslateRegister (tge.Register1));
		}

		public override void Compile (TestIfLessThan tl)
		{
			Append ("Cmp {0}, {1}", TranslateRegister (tl.Register1), TranslateRegister (tl.Register2));
			Append ("Mov {0}, FLAGS", TranslateRegister (tl.Register1));
			Append ("And_Immediate  {0}, 2", TranslateRegister (tl.Register1));
		}

		public override void Compile (TestIfLessThanOrEqual tle)
		{
			Append ("Cmp {0}, {1}", TranslateRegister (tle.Register1), TranslateRegister (tle.Register2));
			Append ("Mov {0}, FLAGS", TranslateRegister (tle.Register1));
			Append ("And_Immediate  {0}, 3", TranslateRegister (tle.Register1));
		}

		public override void Compile (LoadLocal ldloc)
		{
			Append ("Mov a, BP");
			Append ("Sub_Immediate a, {0}", (1 + ldloc.Index) * 2);
			Append ("Load_Word {0}, a", TranslateRegister (ldloc.Register));
		}

		public override void Compile (StoreLocal stloc)
		{
			Append ("Mov a, BP");
			Append ("Sub_Immediate a, {0}", (1 + stloc.Index) * 2);
			Append ("Store_Word a, {0}", TranslateRegister (stloc.Register));
		}

		public override void Compile (StackAlloc stackAlloc)
		{
			Append ("Mov a, BP");
			Append ("Mov b, BP");
			Append ("Sub_Immediate a, {0}", (1 + stackAlloc.Local) * 2);
			Append ("Sub_Immediate b, {0}", stackAlloc.Offset);
			Append ("Store_Word a, b");
		}

		public override void Compile (LoadRegister ldreg)
		{
			Append ("Load_Immediate {0}, {1}", TranslateRegister (ldreg.Register), ldreg.Value);
		}

		public override void Compile (PushArgument pushArg)
		{
			Append ("Push {0}", TranslateRegister (pushArg.Register));
		}

		public override void Compile (PopArgument popArg)
		{
			Append ("Mov a, BP");
			Append ("Add_Immediate a, {0}", (2 + popArg.Index) * 2);
			Append ("Load_Word {0}, a", TranslateRegister (popArg.Register));
		}

		public override void Compile (CallDirect call)
		{
			Append ("Call {0}", call.Target.SymbolName);
			Append ("Add_Immediate SP, {0}", call.Parameters * 2);
		}

		public override void Compile (Bra bra)
		{
			Append ("Jmp {0}", bra.Target.SymbolName);
		}

		public override void Compile (Bz bz)
		{
			Append ("Je {0}", bz.Target.SymbolName);
		}

		public override void Compile (Bnz bnz)
		{
			Append ("Jne {0}", bnz.Target.SymbolName);
		}

		public override void Compile (DefineLabel labelDef)
		{
			Append (".{0}", labelDef.Symbol.Name);
		}

		public override void Compile (CallIndirect call)
		{

		}

		public override void Compile (Ret ret)
		{
			Append ("Add_Immediate SP, {0}", ret.FunctionInfo.LocalSize);
			Append ("Pop BP");
			Append ("Ret");
		}

		public override void Compile (Enter enter)
		{
			accum.AppendLine ("." + enter.FunctionInfo.Name);
			Append ("Push BP");
			Append ("Mov BP, SP");
			Append ("Sub_Immediate SP, {0}", enter.FunctionInfo.LocalSize);
		}

		public override void Compile (AddressOf addressOf)
		{
			Append ("Mov a, BP");
			Append ("Sub_Immediate a, {0}", (1 + addressOf.Local) * 2);
			Append ("Mov {0}, a", TranslateRegister (addressOf.Dest));
		}

		public override void Compile (LoadWord loadWord)
		{
			Append ("Load_Word {0}, {1}", TranslateRegister (loadWord.Destination), TranslateRegister (loadWord.Source));
		}

		public override void Compile (StoreWord storeWord)
		{
			Append ("Store_Word {0}, {1}", TranslateRegister (storeWord.Destination), TranslateRegister (storeWord.Source));
		}

		private void Append (string asm)
		{
			accum.AppendLine ("    " + asm);
		}

		private void Append (string format, params object[] args)
		{
			accum.AppendLine ("    " + String.Format (format, args));
		}

		private string TranslateRegister (int reg) {
			char initial = 'c';
			return ((char)(initial + reg)).ToString ();
		}
	}
}

